package edu.upenn.seas.peerreview.smui

import org.scalatest._

/**
 * Tests the expected functionality of the Board trait.
 */
class BoardImplTest extends FunSuite {
  val kayak : Vessel = Vessel(1)
  val tugboard : Vessel = Vessel(3)

  test("Board has expected size") {
    val board = makeBoard(3, 5)
    assert(board.width === 3)
    assert(board.height === 5)
  }
  
   test("Board starts empty.") {
     assertIsBlank(makeBoard(3, 5), 3, 5)
   }
  
  test("Board throws error on 0 and negative dimension") {
    intercept[AssertionError] {
      makeBoard(0, 3)
    }  
    intercept[AssertionError] {
      makeBoard(5, -5)
    }
    intercept[AssertionError] {
      makeBoard(-5, -5)
    }
    intercept[AssertionError] {
      makeBoard(0, 0)
    }
    makeBoard(1, 1)
  }
  
  test("Change does not mutate original Board instance") {
    val board = makeBoard(3, 5)
    assert(board.examine(Point(1, 1)) == CellStatus.Empty, "Begins empty")
    val boardNew = board+(Point(1,1), CellStatus.Hit)
    assert(board.examine(Point(1, 1)) == CellStatus.Empty, "Still empty")
    assert(boardNew.examine(Point(1, 1)) == CellStatus.Hit, "Reflects Change")
  }
  
  test("Chainable board mutations.") {
    val board = makeBoard(5, 5)
    assertIsBlank(board, 5, 5)
    
    val boardNew = board+(Point(0,1), CellStatus.Miss)+(Point(2, 4),CellStatus.Hit)
    assert(boardNew.examine(Point(0, 1)) == CellStatus.Miss)
    assert(boardNew.examine(Point(2, 4)) == CellStatus.Hit)
  }
  
  test("examine() implicit def ") {
    var board = (makeBoard(5, 6)) + 
      (Point(2, 4), CellStatus.Hit) +
      (Point(1, 5), CellStatus.Ship(kayak))
    assert(board.examine(Point(2,4)) == board.examine(2,4))
    assert(board.examine(Point(1,5)) == board.examine(1,5))
  }
  
  test("+() overloading") {
    var board = (makeBoard(8, 8)) + 
      (Point(2, 4), CellStatus.Hit) +
      (Point(1, 5), CellStatus.Ship(kayak))
    assert(board.examine(Point(2,4)) == board.examine(2,4))
    assert(board.examine(Point(1,5)) == board.examine(1,5))
  }
  
  test("examine() returns Invalid if out of bounds") {
    val board = makeBoard(12, 24)
    assert(board.examine(-1, 23) == CellStatus.Invalid)
    assert(board.examine(5, 24) == CellStatus.Invalid)
    assert(board.examine(-2, 25) == CellStatus.Invalid)
    assert(board.examine(12, 6) == CellStatus.Invalid)
    assert(board.examine(11, -4) == CellStatus.Invalid)
    assert(board.examine(0, 0) == CellStatus.Empty)
    assert(board.examine(0, 23) == CellStatus.Empty)
  }
  
  test("implicit def tuple->Point as arg") {
    // Client must define the implicit def.
    implicit def tupleToPoint(tup : (Int, Int)) = Point(tup._1, tup._2)
    var board = makeBoard(5, 5) + (Point(3, 4), CellStatus.Miss) + 
      ((1, 2), CellStatus.Hit)
    assert(board.examine((3,4)) == CellStatus.Miss)
    assert(board.examine(Point(3,4)) == CellStatus.Miss)
    assert(board.examine((1,2)) == CellStatus.Hit)
    assert(board.examine(Point(1,2)) == CellStatus.Hit)
    assert(board.examine((4, 4)) == CellStatus.Empty)
  }
  
  // Return true if board is blank in the [height, width] range.
  private def assertIsBlank(board : Board, width : Int, height : Int) {
    val result = for (i <- 0 until width; 
                      j <- 0 until height; 
                      if board.examine(i,j) != CellStatus.Empty) yield (i,j)
    assert(result.isEmpty)
  }
  
  // Creates a new instance of the target class for testing. Override for
  // any implementation variations.
  private def makeBoard(x : Int, y : Int) = new BoardImpl(x, y)
}